package com.lemo.smart.util;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileFilter;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 类操作工具类
 * Created by 罗选通 on 2017/12/22.
 */
public class ClassUtil {
  private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);

  /**
   * 获取类加载器
   */
  public static ClassLoader getClassLoader() {
    //获取当前线程的ClassLoader
    return Thread.currentThread().getContextClassLoader();
  }

  /**
   * 加载类
   */
  public static Class<?> loadClass(String className, boolean isInitialized) {
    //加载类需要提供类名和是否初始化的标志
    Class<?> cls;
    try {
      cls = Class.forName(className, isInitialized, getClassLoader());
    } catch (ClassNotFoundException e) {
      LOGGER.error("load class failure", e);
      throw new RuntimeException(e);
    }
    return cls;
  }

  /**
   * 获取包下所有类
   */
  public static Set<Class<?>> getClassSet(String packageName) {
    //根据包名并将其转换为文件路径，读取class文件或jar文件，获取指定的类名去加载类
    Set<Class<?>> classSet = new HashSet<Class<?>>();
    try {
      Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
      while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        if (url != null) {
          String protocol = url.getProtocol();
          if (protocol.equals("file")) {
            String packagePath = url.getPath().replaceAll("%20", " ");
            addClass(classSet, packagePath, packageName);
          } else if (protocol.equals("jar")) {
            JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
            if (jarURLConnection != null) {
              JarFile jarFile = jarURLConnection.getJarFile();
              if (jarFile != null) {
                Enumeration<JarEntry> jarEntries = jarFile.entries();
                while (jarEntries.hasMoreElements()) {
                  JarEntry jarEntry = jarEntries.nextElement();
                  String name = jarEntry.getName();
                  if (name.endsWith(".class")) {
                    String className = name.substring(0, name.lastIndexOf(".")).replaceAll("/", ".");
                    doAddClass(classSet, className);
                  }
                }
              }
            }
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return classSet;
  }

  private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {
    File[] files = new File(packagePath).listFiles(new FileFilter() {
      public boolean accept(File pathname) {
        return (pathname.isFile() & pathname.getName().endsWith(".class") || pathname.isDirectory());
      }
    });

    for (File file : files) {
      String filename = file.getName();
      if (file.isFile()) {
        String classname = filename.substring(0, filename.lastIndexOf("."));
        if (StringUtils.isNotBlank(packageName)) {
          classname = packageName.replaceAll("/",".") + "." + classname;
        }
        doAddClass(classSet, classname);
      } else {
        String subPackagePath = filename;
        if (StringUtils.isNotBlank(packagePath)) {
          subPackagePath = packagePath + "/" + subPackagePath;
        }
        String subPackageName = filename;
        if (StringUtils.isNotBlank(packageName)) {
          subPackageName = packageName + "/" + subPackageName;
        }
        addClass(classSet, subPackagePath, subPackageName);
      }
    }

  }

  private static void doAddClass(Set<Class<?>> classSet, String classname) {
    Class<?> aClass = loadClass(classname, false);
    classSet.add(aClass);
  }


}
